#include <stdlib.h>
#include <typeinfo>
#include "obj_manager.hpp"
#include "and.hpp"
#include "or.hpp"
#include "not.hpp"
#include "xor.hpp"
#include "equal.hpp"

ObjManager::ObjManager()
{
  this->_opType['='] = EQUAL;
  this->_opType['!'] = NOT;
  this->_opType['*'] = AND;
  this->_opType['+'] = OR;
  this->_opType['-'] = XOR;
}

AObject*	ObjManager::getFact(const std::string& name)
{
  if (this->_facts.find(name) != this->_facts.end())
    return this->_facts[name];
  return NULL;
}

bool		ObjManager::createFact(const std::string& name)
{
  if (this->_facts.find(name) == this->_facts.end())
    {
      Fact*	ptr;
      if ((ptr = new Fact()) != NULL)
	{
	  ptr->setName(name);
	  this->_facts[name] = ptr;
	  //	  std::cout << "new var_name: " << name << std::endl;
	  return true;
	}
      else
	{
	  std::cerr << "[ERROR] NOT ENOUGH MEMORY.\n";
	  exit(1);
	}
    }
  return false;
}

ObjManager::operators	ObjManager::isOp(char c) const
{
  std::map<char, operators>::const_iterator	it = this->_opType.find(c);

  if (it != this->_opType.end())
    return it->second;
  return UNDEFINED;
}

AObject*		ObjManager::createOperator(ObjManager::operators obj)
{
  switch (obj)
    {
    case AND:
      //      std::cout << "new AND created" << std::endl;
      return new And();
    case OR:
      //      std::cout << "new OR created" << std::endl;
      return new Or();
    case NOT:
      //      std::cout << "new NOT created" << std::endl;
      return new Not();
    case XOR:
      //      std::cout << "new XOR created" << std::endl;
      return new Xor();
    case EQUAL:
      //      std::cout << "new EQUAL created" << std::endl;
      return new Equal();
    case UNDEFINED:
      break;
    }
  //  std::cout << " ==> UNDEFINED SIGN: This is not an operator" << std::endl;
  return NULL;
}

void			ObjManager::addObj(unsigned int line, AObject* obj)
{
  this->_lineObjs[line].push_back(obj);
}

void			ObjManager::displayThis(AObject* obj)
{
  Fact*			fact_ptr = NULL;

  std::cout << typeid(*obj).name();
  if ((fact_ptr = dynamic_cast<Fact*>(obj)) != NULL)
    std::cout << ":" << fact_ptr->getName();
}

void			ObjManager::displayObjs()
{
  std::map<unsigned int, std::list<AObject*> >::iterator	it = this->_lineObjs.begin();
  std::map<unsigned int, std::list<AObject*> >::iterator	ite = this->_lineObjs.end();
  std::list<AObject*>*						list_ptr;

  std::cout << "Liste des objets: ";
  for (; it != ite; ++it)
    {
      list_ptr = &(it->second);
      std::list<AObject*>::iterator				ot = list_ptr->begin();
      std::list<AObject*>::iterator				ote = list_ptr->end();

      std::cout << std::endl << "l" << it->first << ":\t";
      for (; ot != ote; ++ot)
	{
	  ObjManager::displayThis(*ot);
	  std::cout << "\t";
	}
    }
  std::cout << std::endl;
}

bool			ObjManager::linkObjs()
{
  std::map<unsigned int, std::list<AObject*> >::iterator	it = this->_lineObjs.begin();
  std::map<unsigned int, std::list<AObject*> >::iterator	ite = this->_lineObjs.end();
  unsigned int		line = 1;

  //liste numérotée (copie tmp) pour qcces suivant precedent en mode easy
  for (; it != ite; ++it)
    {
      unsigned int						cpt = 0;
      std::list<AObject*>::iterator				ot = (it->second).begin();
      std::list<AObject*>::iterator				ote = (it->second).end();
      AObject::LinkChain					tmp_map = new std::map<unsigned int, std::pair<AObject*, bool> >;

      if (!tmp_map)
	{
	  std::cout << "Cant allocate memory for new std::map" << std::endl;
	  return false;
	}
      for (; ot != ote; ++ot)
	{
	  ((*tmp_map)[cpt]).first = *ot;
	  ((*tmp_map)[cpt]).second = false;
	  ++cpt;
	}
      if (!this->linkLine(tmp_map))
	return false;
      else
	std::cout << "==> Line " << line << " linked"
		  << "_______________________________"
		  << "_______________________________\n\n";
      delete tmp_map;
      ++line;
    }
  return true;
}

bool			ObjManager::linkLine(AObject::LinkChain tmp_map)
{
  AObject::LinkChainIterator	it;
  AObject::LinkChainIterator	ite;

  while (tmp_map->size() > 1)
    {
      it = tmp_map->begin();
      ite = tmp_map->end();

      // On avance tant que l'on a pas linker
      while (it != ite)
	{
	  if (!((it->second).first)->link(tmp_map, it))
	    {
	      std::cout << "\033[34mPass ";
	      displayThis((it->second).first);
	      std::cout << "\033[0m\n";
	    }
	  else
	    break;
	  ++it;
	}
      
      if (it != ite)
	{
	  /*	  std::cout << "We just linked ";
	  displayThis((it->second).first);
	  std::cout << "\n";*/
	}
      else
	{
	  std::cout << "SYNTAX ERROR: Line cannot be linked?" << std::endl;
	  return false;
	}
      it = tmp_map->begin();
      ite = tmp_map->end();

      // On reparcoure la list pour supprimer les elems appeles par getRequiredLinks
      // et qui sont donc deja linkes a ce moment ci

      for (;it != ite;)
	{
	  if ((it->second).second)
	    {
	      std::cout << "\033[31m\t";
	      displayThis((it->second).first);
	      std::cout << "\033[0m\n";
	      tmp_map->erase(it++);
	    }
	  else
	    ++it;
	}
    }
  return true;
}
